﻿using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Text.RegularExpressions;
using System.Web;

namespace Job.Core.Web.HttpModules
{
    /// <summary>
    /// 使用 gzip/deflate 对 http 输出流进行压缩。
    /// </summary>
    public class Compression : IHttpModule
    {
        #region Properties

        private const string GZIP = "gzip";
        private const string DEFLATE = "deflate";

        private static List<Regex> excludes = new List<Regex>();

        /// <summary>
        /// 需排除压缩的正则表达式，如果启用压缩，部分 ajax 交互部分可能有问题，可以使用排除的 url 禁止压缩这部分。
        /// </summary>
        public static List<Regex> Excludes
        {
            get { return excludes; }           
        }

        #endregion

        #region IHttpModule 成员

        void IHttpModule.Dispose()
        {
            
        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
        }       

        #endregion

        #region Methods

        void context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpApplication context = (HttpApplication)sender;

            foreach (Regex exclude in Excludes)
                if (exclude.IsMatch(context.Request.FilePath))
                    return;
            
            if (IsEncodingAccepted(GZIP))
            {
                context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
                SetEncoding(GZIP);
            }
            else if (IsEncodingAccepted(DEFLATE))
            {
                context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress);
                SetEncoding(DEFLATE);
            }
        }

        /// <summary>
        /// 根据Http请求检查客户端是否支持指定的编码。
        /// </summary>
        /// <param name="encoding">编码。</param>
        /// <returns>支持返回 true，不支持返回 false。</returns>
        private static bool IsEncodingAccepted(string encoding)
        {
            HttpContext context = HttpContext.Current;
            return context.Request.Headers["Accept-encoding"] != null && context.Request.Headers["Accept-encoding"].Contains(encoding);
        }

        /// <summary>
        /// 设置为客户端返回指定的编码。
        /// </summary>
        /// <param name="encoding">编码。</param>
        private static void SetEncoding(string encoding)
        {
            HttpContext.Current.Response.AddHeader("Content-encoding", encoding);
        }

        #endregion
    }
}